home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / www / library / implemen / htanchor.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  26.7 KB  |  1,018 lines

  1. /*      Hypertext "Anchor" Object                               HTAnchor.c
  2. **      ==========================
  3. **
  4. ** An anchor represents a region of a hypertext document which is linked to
  5. ** another anchor in the same or a different document.
  6. **
  7. ** History
  8. **
  9. **         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
  10. **      24-Oct-1991 (JFG), written in C, browser-independant 
  11. **      21-Nov-1991 (JFG), first complete version
  12. **      12-Apr-1994 (GAB), rewrite and debugged for MSDOS.
  13. **
  14. **      (c) Copyright CERN 1991 - See Copyright.html
  15. */
  16.  
  17. #include"capalloc.h"
  18. #include"capstdio.h"
  19. #include <ctype.h>
  20. #include "tcp.h"
  21. #include "HTAnchor.h"
  22. #include "HTUtils.h"
  23. #include "HTParse.h"
  24.  
  25. /*
  26.  *      Abstract hypertext document.  Used to define pointers of appropriate
  27.  *              type.
  28.  */
  29. typedef struct _HyperDoc Hyperdoc;
  30.  
  31. /*
  32.  *      This is a hash table of HTLists containg the addresses of all parent
  33.  *      anchors.  Empty on startup.  This memory appears to never be freed
  34.  *      once created, along with all the lists it contains also....
  35.  */
  36. PRIVATE HTList **adult_table = NULL;
  37.  
  38. /*
  39.  *      This is the size of the adult_table in pointers to the HTList
  40.  *      structure.  Resizable for whatever you need of course.
  41.  *      The original creators intended this number to be prime, but the
  42.  *      need is not evident.
  43.  */
  44. #define HASH_SIZE 101U
  45.  
  46. /*
  47.  *      This is the hasing function used to determine which list in the
  48.  *      adult_table a parent anchor should be put it.  It was simplified for
  49.  *      MSDOS in a drastic way.
  50.  */
  51. #define HASH_FUNCTION(cp_address) ((unsigned short int)strlen(cp_address) *\
  52.     (unsigned short int)toupper(*cp_address) % HASH_SIZE)
  53.  
  54. /*
  55.  *      The following used to be a function, but has been moved to be a
  56.  *      macro for speed.  It simply does a case insensitive string compare.
  57.  */
  58. #define equivalent(cp_str1, cp_str2) (stricmp(cp_str1, cp_str2) ? NO : YES)
  59.  
  60. static HTParentAnchor *HTParentAnchor_new(void) {
  61. /*
  62.  *      Purpose:        Create a new parent anchor that is compeletly empty.
  63.  *      Arguments:      void
  64.  *      Return Value:   HTParentAnchor *        The new parent anchor that
  65.  *                                              was allocated, or NULL.
  66.  *      Remarks/Portability/Dependencies/Restrictions:
  67.  *              Callers should eventually destroy this parent anchor.
  68.  *              See HTAnchor_delete.
  69.  *      Revision History:
  70.  *              04-12-94        Modified for MSDOS by GAB.
  71.  */
  72.     /*
  73.      *      Create the new parent anchor, zero filled.
  74.      */
  75.     auto HTParentAnchor *newAnchor = (HTParentAnchor *)calloc (1,
  76.         sizeof (HTParentAnchor));
  77.  
  78.     /*
  79.      *      If unable, of course return NULL.
  80.      */
  81.     if(newAnchor != NULL)   {
  82.         /*
  83.          *      The new parent anchor is it's own parent, not NULL.
  84.          *      This provides correct functionality in the remaining
  85.          *      HTAnchor functions.
  86.          */
  87.         newAnchor->parent = newAnchor;
  88.         return(newAnchor);
  89.     }
  90.     return(NULL);
  91. }
  92.  
  93. static HTChildAnchor *HTChildAnchor_new(void)   {
  94. /*
  95.  *      Purpose:        Create a new child anchor that is completely empty.
  96.  *      Arguments:      void
  97.  *      Return Value:   HTChildAnchor * The new child anchor, or NULL.
  98.  *      Remarks/Portability/Dependencies/Restrictions:
  99.  *              Callers should eventually destroy this child anchor
  100.  *              by destroying its parent.  See HTAnchor_delete.
  101.  *      Revision History:
  102.  *              04-12-94        Modified for MSDOS by GAB.
  103.  */
  104.     /*
  105.      *      Return the new memory, or NULL.
  106.      */
  107.     return((HTChildAnchor *)calloc (1, sizeof (HTChildAnchor)));
  108. }
  109.  
  110. static HTChildAnchor *HTAnchor_findChild(HTParentAnchor *parent, const char
  111.     *tag)   {
  112. /*
  113.  *      Purpose:        Will find a child in the parent's children and add
  114.  *                              the tag, or will create a new child, add
  115.  *                              the tag, and then add the new child to the
  116.  *                              parent.
  117.  *      Arguments:      parent  The parent anchor of the child.
  118.  *                      tag     The tag value for the child.
  119.  *      Return Value:   HTChildAnchor * The new or old child inside the
  120.  *                                      parents children with the tag, or
  121.  *                                      NULL if nothing done.
  122.  *      Remarks/Portability/Dependencies/Restrictions:
  123.  *              Memory may be allocated.  Callers should eventually destroy
  124.  *              child anchors by calling HTAnchor_delete with the child's
  125.  *              parent.
  126.  *      Revision History:
  127.  *              04-12-94        modified for MSDOS by GAB.
  128.  */
  129.     auto HTChildAnchor *child;
  130.     auto HTList *kids;
  131.  
  132.     /*
  133.      *      Child anchors must always have a parent to own them.
  134.      *      If none, return NULL.
  135.      */
  136.     if(!parent)     {
  137. #ifndef RELEASE
  138.         if(TRACE)       {
  139.             printf ("HTAnchor_findChild called with NULL parent."
  140.                 "\n");
  141.         }
  142. #endif /* RELEASE */
  143.         return(NULL);
  144.     }
  145.  
  146.     /*
  147.      *      If the parent has children, search them for a duplicate
  148.      *      child.
  149.      */
  150.     if(kids = parent->children)     {
  151.         /*
  152.          *      If there was a tag passed in.
  153.          */
  154.         if(tag && *tag) {
  155.             while(child = HTList_nextObject(kids))  {
  156.                 /*
  157.                  *      If the tags are the same, we assume
  158.                  *              the same anchor.  This is
  159.                  *              case insensitive.
  160.                  */
  161.                 if(equivalent(child->tag, tag)) {
  162. #ifndef RELEASE
  163.                     if(TRACE)       {
  164.                         fprintf (stderr, "Child anchor "
  165.                             "%p of parent %p with "
  166.                             "name `%s' already "
  167.                             "exists.\n", (void*)
  168.                             child, (void*)parent,
  169.                             tag);
  170.                     }
  171. #endif /* RELEASE */
  172.                     /*
  173.                      *      return the child that was
  174.                      *      found.  No anchor need be
  175.                      *      created since the
  176.                      *      anchor already has it.
  177.                      */
  178.                     return(child);
  179.                 }
  180.             }
  181.         }
  182.     }
  183.     else    {
  184.         /*
  185.          *      The parent has no list of children, creat the family.
  186.          *      Empty as of now.
  187.          */
  188.         parent->children = HTList_new();
  189.     }
  190.  
  191.     /*
  192.      *      Create a new empty child anchor.
  193.      */
  194.     child = HTChildAnchor_new();
  195. #ifndef RELEASE
  196.     if(TRACE)       {
  197.         fprintf(stderr, "new Anchor %p named `%s' is child of %p\n",
  198.             (void*)child, (int)tag ? tag : (const char *)"",
  199.             (void*)parent);
  200.        }
  201. #endif /* RELEASE */
  202.  
  203.     /*
  204.      *      Add the new child to the parent's list.
  205.      *      Let the child know who its parent is.
  206.      *      Tag the child.
  207.      */
  208.     HTList_addObject(parent->children, child);
  209.     child->parent = parent;
  210.     StrAllocCopy(child->tag, tag);
  211.  
  212.     /*
  213.      *      This is the return for the newly created child.
  214.      */
  215.     return(child);
  216. }
  217.  
  218. extern HTChildAnchor *HTAnchor_findChildAndLink(HTParentAnchor *parent, const
  219.     char *tag, const char *href, HTLinkType *ltype) {
  220. /*
  221.  *      Purpose:        Create a new child anchor (or an already existing one)
  222.  *                              with the given parent and possibly link to a
  223.  *                              relatively named anchor.
  224.  *      Arguments:      parent  The parent anchor of the child.  May not be
  225.  *                              NULL.
  226.  *                      tag     The tag value for the child.  May be NULL.
  227.  *                      href    The hypertext reference to which to link.
  228.  *                              May be NULL.
  229.  *                      ltype   The type of link to make (destination anchor
  230.  *                              etc.)  May be NULL.
  231.  *      Return Value:   HTChildAnchor * The new or old child inside the
  232.  *                                      parents children with the tag, or
  233.  *                                      NULL if nothing done.
  234.  *      Remarks/Portability/Dependencies/Restrictions:
  235.  *              Memory may be allocated.  Callers should eventually destroy
  236.  *              child anchors by calling HTAnchor_delete with the child's
  237.  *              parent.
  238.  *      Revision History:
  239.  *              04-12-94        modified for MSDOS by GAB.
  240.  */
  241.  
  242.     /*
  243.      *      This may create a new child or return an older one.
  244.      */
  245.     auto HTChildAnchor *child = HTAnchor_findChild(parent, tag);
  246.  
  247.     /*
  248.      *      If there is a hypertext reference.
  249.      *      Construct it's address relative to the parent.
  250.      */
  251.     if (href && *href)      {
  252.         /*
  253.          *      Get the parent's address.  Allocated memory returned.
  254.          */
  255.         auto char *relative_to = HTAnchor_address((HTAnchor *)parent);
  256.  
  257.         /*
  258.          *      Get the parsed address relatively.  Allocated memory
  259.          *              returned.
  260.          */
  261.         auto char *parsed_address = HTParse(href, relative_to,
  262.             PARSE_ALL);
  263.  
  264.         /*
  265.          *      Find the destination of the paresed relatvie address.
  266.          *      This could return an already existing anchor, either
  267.          *      parent or child, or new memory could be allocated.
  268.          */
  269.         auto HTAnchor *dest = HTAnchor_findAddress(parsed_address);
  270.  
  271.         /*
  272.          *      Perform the linkage of the child to the destination
  273.          *      with the appropriate link type.
  274.          */
  275.         HTAnchor_link((HTAnchor *)child, dest, ltype);
  276.  
  277.         /*
  278.          *      Free up the memory allocated by the WWW functions.
  279.          */
  280.         free(parsed_address);
  281.         free(relative_to);
  282.     }
  283.  
  284.     /*
  285.      *      Return the child, wether or not created and wether or not
  286.      *      linked.
  287.      */
  288.     return(child);
  289. }
  290.  
  291.  
  292. extern HTAnchor *HTAnchor_findAddress(const char *cp_address)   {
  293. /*
  294.  *      Purpose:        Create a new or find an old named anchor.
  295.  *      Arguments:      cp_address      The address (absolute URL?) of the
  296.  *                                      anchor to find or create.
  297.  *      Return Value:   HTAnchor *      The anchor, regardless if created or
  298.  *                                      if found.
  299.  *      Remarks/Portability/Dependencies/Restrictions:
  300.  *      Revision History:
  301.  *              ??-??-??        created
  302.  *              03-28-94        modified for DosLynx
  303.  */
  304.  
  305.     /*
  306.      *      Find out if the anchor has a tag on it.
  307.      *      This is allocated memory.
  308.      */
  309.     auto char *cp_tag = HTParse(cp_address, "", PARSE_ANCHOR);
  310.  
  311.     /*
  312.      *      If there exists a tag in the anchor, the address represents
  313.      *      what is called a sub-anchor (a child for sure).
  314.      *      First, load the parent anchor, and then create a child
  315.      *      anchor within the document.
  316.      */
  317.     if(cp_tag != NULL && *cp_tag != '\0')   {
  318.         /*
  319.          *      Create the address without the tag.
  320.          *      This is allocated memory.
  321.          */
  322.         auto char *cp_docAddress = HTParse(cp_address, "",
  323.             PARSE_ACCESS | PARSE_HOST | PARSE_PATH |
  324.             PARSE_PUNCTUATION);
  325.         /*
  326.          *      Load the parent (no longer has the tag) recursively.
  327.          *      Could be a new or old parent.
  328.          */
  329.         auto HTParentAnchor *HTPAp_foundParent = (HTParentAnchor *)
  330.             HTAnchor_findAddress(cp_docAddress);
  331.  
  332.         /*
  333.          *      Find the anchor within the parent anchor with the
  334.          *      particular tagged anchor.  If none exists, one will
  335.          *      be created.
  336.          */
  337.         auto HTChildAnchor *HTCAp_foundAnchor = HTAnchor_findChild(
  338.             HTPAp_foundParent, cp_tag);
  339.  
  340.         /*
  341.          *      Free memory allocated by called functions.
  342.          */
  343.         free(cp_docAddress);
  344.         free(cp_tag);
  345.  
  346.         /*
  347.          *      Return the child anchor that was found or created.
  348.          */
  349.         return((HTAnchor *)HTCAp_foundAnchor);
  350.     }
  351.     /*
  352.      *      Otherwise there is no tagged anchor.  Just load the parent.
  353.      *      If there was a tag, then we reach here anyway via recursion.
  354.      */
  355.     else    {
  356.         auto unsigned short int usi_hash;
  357.         auto HTList *HTLp_adults;
  358.         auto HTList *HTLp_grownups;
  359.         auto HTParentAnchor *HTPAp_foundAnchor;
  360.  
  361.         /*
  362.          *      Free the tag if it exists, won't need it.
  363.          */
  364.         if(cp_tag != NULL)      {
  365.             free(cp_tag);
  366.         }
  367.  
  368.         /*
  369.          *      Select a particular list from the hash table.
  370.          */
  371.         usi_hash = HASH_FUNCTION(cp_address);
  372.  
  373.         /*
  374.          *      If the adult table has not yet been created, create
  375.          *      and initialize it.
  376.          */
  377.         if(adult_table == NULL) {
  378.             adult_table = (HTList**)calloc(HASH_SIZE,
  379.                 sizeof(HTList*));
  380.         }
  381.  
  382.         /*
  383.          *      If a particular list in the hash table has not been
  384.          *      created, do so.
  385.          */
  386.         if(adult_table[usi_hash] == NULL)       {
  387.             adult_table[usi_hash] = HTList_new();
  388.         }
  389.  
  390.         /*
  391.          *      We now have an adults list to look for our address.
  392.          */
  393.         HTLp_adults = adult_table[usi_hash];
  394.  
  395.         /*
  396.          *      Search the list for the anchor.
  397.          */
  398.         HTLp_grownups = HTLp_adults;
  399.         while(NULL != (HTPAp_foundAnchor = HTList_nextObject(
  400.             HTLp_grownups)))        {
  401.             /*
  402.              *      Compare the addresses to see if the same.
  403.              *      The compare is case insensitive.
  404.              *      Here is some very slow code....
  405.              */
  406.             if(equivalent(HTPAp_foundAnchor->address,
  407.                 cp_address))    {
  408. #ifndef RELEASE
  409.                 if(TRACE)       {
  410.                     fprintf(stderr, "Anchor %p "
  411.                         "with address `%s' "
  412.                         "already exists.\n",
  413.                         (void *)
  414.                         HTPAp_foundAnchor,
  415.                         cp_address);
  416.                 }
  417. #endif /* RELEASE */
  418.                 /*
  419.                  *      Return the found anchor in
  420.                  *      the hash table.
  421.                  */
  422.                 return((HTAnchor *)HTPAp_foundAnchor);
  423.             }
  424.         }
  425.  
  426.         /*
  427.          *      An anchor was not found in the hast list with the same
  428.          *      address.  Create a new entry.
  429.          */
  430.         HTPAp_foundAnchor = HTParentAnchor_new();
  431. #ifndef RELEASE
  432.         if(TRACE)       {
  433.             fprintf(stderr, "New anchor %p has hash %u and "
  434.                 "address `%s'\n", (void*)HTPAp_foundAnchor,
  435.                 usi_hash, cp_address);
  436.         }
  437. #endif /* RELEASE */
  438.  
  439.         /*
  440.          *      Allocate the address space for the new anchor and
  441.          *      copy it in.
  442.          */
  443.         StrAllocCopy(HTPAp_foundAnchor->address, cp_address);
  444.  
  445.         /*
  446.          *      Add the new anchor to the hash list.
  447.          */
  448.         HTList_addObject(HTLp_adults, HTPAp_foundAnchor);
  449.  
  450.         /*
  451.          *      Return the newly created anchor.
  452.          */
  453.         return((HTAnchor *)HTPAp_foundAnchor);
  454.     }
  455. }
  456.  
  457.  
  458. static void deleteLinks(HTAnchor *me)   {
  459. /*
  460.  *      Purpose:    Remove the anchor me from all of it's destination
  461.  *                parent anchor's sources.
  462.  *            If the destination parent is not also loaded, then
  463.  *                call HTAnchor_delete for it also.
  464.  *      Arguments:    me    The anchor (child or parent) for which to
  465.  *                unregister will all the parent anchors that
  466.  *                are me's destination.
  467.  *      Return Value:    void
  468.  *      Remarks/Portability/Dependencies/Restrictions:
  469.  *        This function, combined with HTAnchor_delete, form a
  470.  *            recursive relationship to clean up all unloaded
  471.  *            parent anchors.
  472.  *      Revision History:
  473.  *              04-12-94        modified for MSDOS by GAB
  474.  */
  475.  
  476.     /*
  477.      *      Anchor is NULL, do nothing.
  478.      */
  479.     if(me == NULL)  {
  480.         return;
  481.     }
  482.  
  483.     /*
  484.      *      Unregister us with our mainLink destination anchor's parent.
  485.      */
  486.     if(me->mainLink.dest != NULL)   {
  487.         auto HTParentAnchor *parent = me->mainLink.dest->parent;
  488.  
  489.         /*
  490.          *      Remove us from the parent's sources so that the
  491.          *    parent knows one less anchor is it's destination.
  492.          */
  493.         if(!HTList_isEmpty(parent->sources))    {
  494.             /*
  495.              *    Really should only need to deregister once.
  496.              */
  497.             HTList_removeObject(parent->sources, (void *)me);
  498.         }
  499.  
  500.         /*
  501.          *      Test here to avoid calling overhead.
  502.          *    If the parent has no loaded document, then we should
  503.          *    tell it to attempt to delete itself.
  504.          *    Don't do this jazz if the anchor passed in is the same
  505.          *    as the anchor to delete.
  506.          *    Also, don't do this if the destination parent is our
  507.          *    parent.
  508.          */
  509.         if(parent->document == NULL && parent != (HTParentAnchor *)me
  510.             && me->parent != parent)    {
  511.             HTAnchor_delete(parent);
  512.         }
  513.  
  514.         /*
  515.          *    At this point, we haven't a mainLink.  Set it to be
  516.          *    so.  Leave the HTAtom pointed to by type up to WWW to
  517.          *    handle.
  518.          */
  519.         me->mainLink.dest = NULL;
  520.         me->mainLink.type = NULL;
  521.     }
  522.  
  523.     /*
  524.      *      Check for extra destinations in our links list.
  525.      */
  526.     if(!HTList_isEmpty(me->links))  {
  527.         auto HTLink *target;
  528.         auto HTParentAnchor *parent;
  529.  
  530.         /*
  531.          *    Take out our extra non mainLinks one by one, calling
  532.          *    their parents to know that they are no longer the
  533.          *    destination of me's anchor.
  534.          */
  535.         while(target = (HTLink *)HTList_removeLastObject(me->links))
  536.         {
  537.             parent = target->dest->parent;
  538.  
  539.             if(!HTList_isEmpty(parent->sources))
  540.             {
  541.                 /*
  542.                  *    Only need to tell destination parent
  543.                  *    anchor once.
  544.                  */
  545.                 HTList_removeObject(parent->sources,
  546.                     (void *)me);
  547.             }
  548.  
  549.             /*
  550.              *      Avoid calling overhead.
  551.              *    If the parent hasn't a loaded document, then
  552.              *    we will attempt to have the parent delete
  553.              *    itself.
  554.              *    Don't call twice if this is the same anchor
  555.              *    that we are trying to delete.
  556.              *    Also, Don't do this if we are trying to delete
  557.              *    our parent.
  558.              */
  559.             if(parent->document == NULL && (HTParentAnchor *)me
  560.                 != parent && me->parent != parent)    {
  561.                 HTAnchor_delete(parent);
  562.             }
  563.         }
  564.         /*
  565.          *    At this point, me no longer has any destinations in
  566.          *    out links list.  Get rid of it.
  567.          */
  568.         HTList_delete(me->links);
  569.         me->links = NULL;
  570.     }
  571.  
  572.     /*
  573.      *    Catch in case links list exists but nothing in it.
  574.      */
  575.     if(me->links != NULL)    {
  576.         HTList_delete(me->links);
  577.         me->links = NULL;
  578.     }
  579. }
  580.  
  581. extern BOOL HTAnchor_delete(HTParentAnchor *me) {
  582. /*
  583.  *    Purpose:    Delete a parent anchor, it's children, and any
  584.  *                documents pointed at by either that also
  585.  *                do not have a valid HyperDoc loaded.
  586.  *    Arguments:    me    The parent anchor to begin deletion at.
  587.  *    Return Value:    BOOL    YES the parent anchor me was removed
  588.  *                    completely.
  589.  *                NO the parent anchor does not exist, has
  590.  *                    a valid HyperDoc loaded, or is still
  591.  *                    the destination of another loaded
  592.  *                    anchor.
  593.  *    Remarks/Portability/Dependencies/Restrictions:
  594.  *        Together with the deleteLinks function we form a type of
  595.  *            recursion.
  596.  *        We don't release the links member of HTAnchor here, since it
  597.  *            is really done in deleteLinks.
  598.  *    Revision History:
  599.  *        04-12-94    modified by GAB for MSDOS.
  600.  *                Lots of memory leaks and assignment problems
  601.  *                fixed.
  602.  */
  603.     auto HTChildAnchor *child;
  604.  
  605.     /*
  606.      *      Do nothing if nothing to do.
  607.      */
  608.     if(me == NULL)  {
  609.         return NO;
  610.     }
  611.  
  612. #ifndef RELEASE
  613.     if(TRACE)       {
  614.         printf("Deleting %s\n", me->address);
  615.     }
  616. #endif /* RELEASE */
  617.     /*
  618.      *      Don't delete if document is loaded
  619.      */
  620.     if(me->document != NULL)        {
  621. #ifndef RELEASE
  622.         if(TRACE)       {
  623.             printf("Can't delete, still loaded.\n");
  624.         }
  625. #endif /* RELEASE */
  626.         return NO;
  627.     }
  628.  
  629.     /*
  630.      *      Recursively try to delete destination anchors of this parent.
  631.      *    In any event, this will tell all destination anchors that we
  632.      *    no longer consider them a destination.
  633.      */
  634.     deleteLinks((HTAnchor *)me);
  635.  
  636.     /*
  637.      *      There are still incoming links to this one (we are the
  638.      *    destination of another anchor).
  639.      *      Don't actually delete this anchor, but children are OK to
  640.      *      delete their links.
  641.      */
  642.     if(!HTList_isEmpty(me->sources))        {
  643.         /*
  644.          *      Delete all outgoing links from children, do not
  645.          *    delete the children though.
  646.          */
  647.         auto HTList *kids = me->children;
  648.  
  649.         if(!HTList_isEmpty(kids))       {
  650.             while(child = (HTChildAnchor *)HTList_nextObject(
  651.                 kids))  {
  652.                 if(child != NULL)       {
  653.                     deleteLinks((HTAnchor *)
  654.                         child);
  655.                 }
  656.             }
  657.         }
  658.  
  659.         /*
  660.          *      Parent not deleted.
  661.          */
  662. #ifndef RELEASE
  663.         if(TRACE)       {
  664.             printf("Can't delete, still has sources.\n");
  665.         }
  666. #endif /* RELEASE */
  667.         return NO;
  668.     }
  669.  
  670.     /*
  671.      *      No more incoming links : kill everything
  672.      *      First, recursively delete children and their links.
  673.      */
  674.     if(!HTList_isEmpty(me->children))       {
  675.         while(child = (HTChildAnchor *)HTList_removeLastObject(me->
  676.             children))      {
  677.             if(child != NULL)       {
  678.                 deleteLinks((HTAnchor *)child);
  679.                 if(child->tag != NULL)  {
  680.                     free(child->tag);
  681.                 }
  682.                 free(child);
  683.             }
  684.         }
  685.     }
  686.  
  687.     /*
  688.      *      Delete our empty list of children.
  689.      */
  690.     if(me->children != NULL)        {
  691.         HTList_delete(me->children);
  692.     }
  693.     /*
  694.      *    Delete our empty list of sources.
  695.      */
  696.     if(me->sources != NULL) {
  697.         HTList_delete(me->sources);
  698.     }
  699.     /*
  700.      *    Delete the methods list.
  701.      */
  702.     if(me->methods != NULL) {
  703.         /*
  704.          *      I guess leave what methods points to up in MEM for
  705.          *              WWW code.
  706.          */
  707.         HTList_delete(me->methods);
  708.     }
  709.     /*
  710.      *    Free up all allocated members.
  711.      */
  712.     if(me->isIndexAction != NULL)   {
  713.         free(me->isIndexAction);
  714.     }
  715.     if(me->title != NULL)   {
  716.         free(me->title);
  717.     }
  718.     if(me->physical != NULL)        {
  719.         free(me->physical);
  720.     }
  721.     /*
  722.      *    Remove ourselves from the hash table's list.
  723.      */
  724.     if(adult_table != NULL) {
  725.         auto unsigned short int usi_hash = HASH_FUNCTION(me->address);
  726.         if(adult_table[usi_hash] != NULL)       {
  727.             HTList_removeObject(adult_table[usi_hash],
  728.                 (void *)me);
  729.         }
  730.     }
  731.     /*
  732.      *    Free our address, saved till last to figure our position in
  733.      *    the hash table.
  734.      */
  735.     if(me->address != NULL) {
  736.         free(me->address);
  737.     }
  738.  
  739.     /*
  740.      *      Devise a way to clean out the HTFormat if no longer needed
  741.      *      (ref count?)  Leave it up to WWW since is an HTAtom....
  742.      */
  743.  
  744.     /*
  745.      *    Finally, kill the parent anchor passed in.
  746.      */
  747.     free(me);
  748.  
  749.     /*
  750.      *      Parent was deleted.
  751.      */
  752.     return(YES);
  753. }
  754.  
  755.  
  756. /*              Move an anchor to the head of the list of its siblings
  757. **              ------------------------------------------------------
  758. **
  759. **      This is to ensure that an anchor which might have already existed
  760. **      is put in the correct order as we load the document.
  761. */
  762.  
  763. void HTAnchor_makeLastChild
  764.   ARGS1(HTChildAnchor *,me)
  765. {
  766.   if (me->parent != (HTParentAnchor *) me) {  /* Make sure it's a child */
  767.     HTList * siblings = me->parent->children;
  768.     HTList_removeObject (siblings, me);
  769.     HTList_addObject (siblings, me);
  770.   }
  771. }
  772.  
  773. /*      Data access functions
  774. **      ---------------------
  775. */
  776.  
  777. PUBLIC HTParentAnchor * HTAnchor_parent
  778.   ARGS1 (HTAnchor *,me)
  779. {
  780.   return me ? me->parent : NULL;
  781. }
  782.  
  783. void HTAnchor_setDocument
  784.   ARGS2 (HTParentAnchor *,me, HyperDoc *,doc)
  785. {
  786.   if (me)
  787.     me->document = doc;
  788. }
  789.  
  790. HyperDoc * HTAnchor_document
  791.   ARGS1 (HTParentAnchor *,me)
  792. {
  793.   return me ? me->document : NULL;
  794. }
  795.  
  796.  
  797. /* We don't want code to change an address after anchor creation... yet ?
  798. void HTAnchor_setAddress
  799.   ARGS2 (HTAnchor *,me, char *,addr)
  800. {
  801.   if (me)
  802.     StrAllocCopy (me->parent->address, addr);
  803. }
  804. */
  805.  
  806. char * HTAnchor_address
  807.   ARGS1 (HTAnchor *,me)
  808. {
  809.   char *addr = NULL;
  810.   if (me) {
  811.     if (((HTParentAnchor *) me == me->parent) ||
  812.     !((HTChildAnchor *) me)->tag) {  /* it's an adult or no tag */
  813.       StrAllocCopy (addr, me->parent->address);
  814.     }
  815.     else {  /* it's a named child */
  816.       addr = malloc (2 + strlen (me->parent->address)
  817.              + strlen (((HTChildAnchor *) me)->tag));
  818.       if (addr == NULL) outofmem(__FILE__, "HTAnchor_address");
  819.       sprintf (addr, "%s#%s", me->parent->address,
  820.            ((HTChildAnchor *) me)->tag);
  821.     }
  822.   }
  823.   return addr;
  824. }
  825.  
  826.  
  827.  
  828. void HTAnchor_setFormat
  829.   ARGS2 (HTParentAnchor *,me, HTFormat ,form)
  830. {
  831.   if (me)
  832.     me->format = form;
  833. }
  834.  
  835. HTFormat HTAnchor_format
  836.   ARGS1 (HTParentAnchor *,me)
  837. {
  838.   return me ? me->format : NULL;
  839. }
  840.  
  841.  
  842.  
  843. void HTAnchor_setIndex
  844.   ARGS2 (HTParentAnchor *,me, const char *,address)
  845. {
  846. #ifndef RELEASE
  847.     if(TRACE)       {
  848.         fprintf(stderr, "HTAnchor_setIndex:  %s is address\n", address);
  849.     }
  850. #endif /* RELEASE */
  851.  
  852.   if (me)       {
  853.     me->isIndex = YES;
  854.     if(address != NULL) {
  855.     StrAllocCopy((me->isIndexAction), address);
  856.     }
  857.     else        {
  858.     me->isIndexAction = NULL;
  859.     }
  860.   }
  861. }
  862.  
  863. BOOL HTAnchor_isIndex
  864.   ARGS1 (HTParentAnchor *,me)
  865. {
  866.   return me ? me->isIndex : NO;
  867. }
  868.  
  869.  
  870.  
  871. BOOL HTAnchor_hasChildren
  872.   ARGS1 (HTParentAnchor *,me)
  873. {
  874.   return me ? ! HTList_isEmpty(me->children) : NO;
  875. }
  876.  
  877. /*      Title handling
  878. */
  879. CONST char * HTAnchor_title
  880.   ARGS1 (HTParentAnchor *,me)
  881. {
  882.   return me ? me->title : 0;
  883. }
  884.  
  885. void HTAnchor_setTitle
  886.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  887. {
  888.   StrAllocCopy(me->title, title);
  889. }
  890.  
  891. void HTAnchor_appendTitle
  892.   ARGS2(HTParentAnchor *,me, CONST char *,title)
  893. {
  894.   StrAllocCat(me->title, title);
  895. }
  896.  
  897. /*      Link me Anchor to another given one
  898. **      -------------------------------------
  899. */
  900.  
  901. BOOL HTAnchor_link
  902.   ARGS3(HTAnchor *,source, HTAnchor *,destination, HTLinkType *,type)
  903. {
  904.   if (! (source && destination))
  905.     return NO;  /* Can't link to/from non-existing anchor */
  906. #ifndef RELEASE
  907.   if (TRACE) printf ("Linking anchor %p to anchor %p\n", source, destination);
  908. #endif /* RELEASE */
  909.   if (! source->mainLink.dest) {
  910.     source->mainLink.dest = destination;
  911.     source->mainLink.type = type;
  912.   } else {
  913.     HTLink * newLink = (HTLink *) malloc (sizeof (HTLink));
  914.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_link");
  915.     newLink->dest = destination;
  916.     newLink->type = type;
  917.     if (! source->links)
  918.       source->links = HTList_new ();
  919.     HTList_addObject (source->links, newLink);
  920.   }
  921.   if (!destination->parent->sources)
  922.     destination->parent->sources = HTList_new ();
  923.   HTList_addObject (destination->parent->sources, source);
  924.   return YES;  /* Success */
  925. }
  926.  
  927.  
  928. /*      Manipulation of links
  929. **      ---------------------
  930. */
  931.  
  932. HTAnchor * HTAnchor_followMainLink
  933.   ARGS1 (HTAnchor *,me)
  934. {
  935.   return me->mainLink.dest;
  936. }
  937.  
  938. HTAnchor * HTAnchor_followTypedLink
  939.   ARGS2 (HTAnchor *,me, HTLinkType *,type)
  940. {
  941.   if (me->mainLink.type == type)
  942.     return me->mainLink.dest;
  943.   if (me->links) {
  944.     HTList *links = me->links;
  945.     HTLink *link;
  946.     while (link = HTList_nextObject (links))
  947.       if (link->type == type)
  948.     return link->dest;
  949.   }
  950.   return NULL;  /* No link of me type */
  951. }
  952.  
  953.  
  954. /*      Make main link
  955. */
  956. BOOL HTAnchor_makeMainLink
  957.   ARGS2 (HTAnchor *,me, HTLink *,movingLink)
  958. {
  959.   /* Check that everything's OK */
  960.   if (! (me && HTList_removeObject (me->links, movingLink)))
  961.     return NO;  /* link not found or NULL anchor */
  962.   else {
  963.     /* First push current main link onto top of links list */
  964.     HTLink *newLink = (HTLink*) malloc (sizeof (HTLink));
  965.     if (newLink == NULL) outofmem(__FILE__, "HTAnchor_makeMainLink");
  966.     memcpy (newLink, & me->mainLink, sizeof (HTLink));
  967.     HTList_addObject (me->links, newLink);
  968.  
  969.     /* Now make movingLink the new main link, and free it */
  970.     memcpy (& me->mainLink, movingLink, sizeof (HTLink));
  971.     free (movingLink);
  972.     return YES;
  973.   }
  974. }
  975.  
  976.  
  977. /*      Methods List
  978. **      ------------
  979. */
  980.  
  981. PUBLIC HTList * HTAnchor_methods ARGS1(HTParentAnchor *, me)
  982. {
  983.     if (!me->methods) {
  984.     me->methods = HTList_new();
  985.     }
  986.     return me->methods;
  987. }
  988.  
  989. /*      Protocol
  990. **      --------
  991. */
  992.  
  993. PUBLIC void * HTAnchor_protocol ARGS1(HTParentAnchor *, me)
  994. {
  995.     return me->protocol;
  996. }
  997.  
  998. PUBLIC void HTAnchor_setProtocol ARGS2(HTParentAnchor *, me,
  999.     void*,  protocol)
  1000. {
  1001.     me->protocol = protocol;
  1002. }
  1003.  
  1004. /*      Physical Address
  1005. **      ----------------
  1006. */
  1007.  
  1008. PUBLIC char * HTAnchor_physical ARGS1(HTParentAnchor *, me)
  1009. {
  1010.     return me->physical;
  1011. }
  1012.  
  1013. PUBLIC void HTAnchor_setPhysical ARGS2(HTParentAnchor *, me,
  1014.     char *, physical)
  1015. {
  1016.     StrAllocCopy(me->physical, physical);
  1017. }
  1018.